1. Redis Mass Insertion
有时候需要将原先存有的大量数据迁移到新的redis实例,redis提供一些方案可以让这个过程更快
通过redis-cli一个一个操作太慢
通过pipeline操作,又会阻塞服务器
大数据量插入时,先按官网提到的协议生成对应格式的文本文件,然后使用redis-cli的管道模式批量导入
2. Partitioning
将数据分布到不同的redis实例
2.1 分片策略
范围分片: 例如根据用户id的区间决定数据划分到哪个实例
hash分片: 先使用哈希函数求得哈希值,再通过取模根据结果例如介于0-3之间决定数据存储在哪个实例.少数客户端在这基础上实现了连续哈希
客户端分片: 在客户端就决定好读写的实例
代理分片: 客户端发送请求到代理,由代理决定实际的redis实例并返回响应给客户端,例如Twemproxy就应用这种模式
查询路由: 客户端发送请求到随机的一个redis实例,redis再转发请求到正确的节点.Redis Cluster 使用了这种模式,不同的是将客户端的连接重定向到正确的redis实例上而不是直接转发.
2.2 分片缺点
分片后,不能直接对多个key一次操作
事务不能对多个key操作
像有序集合数据集被包含在一个大key中无法对内部key进行分片
增加操作复杂度,例如备份数据,需要整合各个实例的持久化文件
增加或减少容量比较复杂,Redis Cluster会重新平衡数据,当增加或者移除节点时.而使用客户端或代理分片方式则难以做到,Pre-sharding技术通过迁移实例的方式实现
3. Distributed locks
已经有多种库实现了分布式redis锁管理,具体https://redis.io/topics/distlock
3.1 安全性和活跃度保证
安全性: 互斥,同一个时刻只能有一个客户端拥有锁
死锁的释放: 例如当客户端锁住资源发生崩溃而其他客户端获取锁时
故障容错: 只要大部分redis节点存活,客户端就能进行正常的获取释放锁
3.2 故障转移缺陷例子
客户端A从主库获取对资源a的锁
对key的写入发送到从库前,主库崩溃
从库被提升为主库
客户端B从主库获取对资源a的锁,此时就违背了安全性原则
3.3 单实例的正确用法
使用setnx设置key的值,值必须全局唯一,释放锁时检查key是否存在,值是否与预期一致(第二条解释)
当客户端获取的锁的key带有过期时间.直接使用del最后释放锁的方式,如果过程中因为一些耗时操作导致key过期,此时其他客户端能够获取到锁,则最后del释放锁会把其他客户端的锁也释放.所以通过设置锁key的值作为签名并在最后使用del释放时做检查
使锁key的值唯一可以使用rc4根据具体信息生成对应随机字符串
3.4 Redlock algorithm
假设有5个redis独立主库
客户端先获取当前的时间毫秒级
顺序获取5个实例的锁,同样的key名和随机值.
客户端获取锁时,会设置一个相对锁过期时间很小的超时时间,如果一个实例获取不到锁超时则立刻获取下一个实例的
顺序获取实例锁时,锁的有效时间会逐渐递减,以最后获取实例的锁有效时间为准,最后每个实例锁的过期时间会是一致
如果已经存在N/2+1实例的锁key或者锁过期,则放弃获取所有实例的锁的操作.这就可以实现互斥原则,当一个客户端获取成功后,其他客户端可以因为没有获取到足够实例的锁而放弃
该算法基于假设所有机器和进程的时钟频率一致或相对于锁过期时间产生的误差可以忽略不计
当获取锁失败时,需要及时释放已获取的部分实例锁,可以避免需要等到key过期才能再次获取锁
文档对安全性和可用性进行了讨论,具体可以看文档
提高锁的性能可以通过使用非阻塞模式发送所有命令,再读取检查
需要设置持久化参数fsync=always避免断电或其他灾难后重启key丢失问题
算法对于断电或灾难重启后的实例不再参与现有活跃的锁.如果客户端A获取到3/5实例锁,而重启新增了一个实例,此时存在N/2+1实例的锁key条件不存在,其他客户端又可以获取锁了.解决问题的方法时,重启后保持一段不可用时间大于其他锁的过期时间.这里会引入一个问题就是如果多个实例重启,在这个不可用期间,意味着新的获取锁可能失败.
扩展可以考虑可重入锁的实现
4. Redis Keyspace Notifications
key空间报告通过发布订阅模式实现,默认不开启,可以通过配置文件开启
接收对key产生实际操作的事件
5. Secondary indexing with Redis
redis主要通过key来获取数据,利用redis的一些数据结构可以创建二级索引
5.1 有序集合的数值索引
通过有序集合的分值对数据对象进行索引
常见的操作为,hash结构存储数据对象集合,有序集合对数据对象创建索引
如果能将多维数据转为线性,则可以利用有序集合对数据进行list索引
两个分值一样的,则通过C函数memcmp比较
5.2 字典索引
ZRANGEBYLEX可以对值进行检索,包括或排除,可以利用在自动补全场景
可以再给值加上频率条件
考虑大小写条件时,可以按小写:频率:大写的方式存储值
使用组合索引,实际就是将多个字段信息组合后存储为有序集合
只要找到一种规则就可以合理利用ZRANGEBYLEX对数据进行查询
6. Replication
主库发送将命令实时发送给从库
主从连接断开时会重连,并找回连接断开期间主库命令重新同步
如果找回断开期间部分的命令失败,则执行全部同步,具体由主库发送快照给从库,然后继续保持同步
从库复制异步进行,从库返回确认也是异步进行
如果从库落后主库,可以根据配置决定此时从库是否还可以使用旧的数据
主从复制的一个好处是可以避免主库持久化总是需要将数据写入磁盘,可以通过从库复制实时保存.然而需要注意的是,如果重启主库,主库数据集为空,从库同步复制主库时,从库数据也会被清空
建议在主从都开启持久化,或者如果不开启持久化则要避免重启机器后自动重启服务
每一个主库有一个replication id标识,对每个发送给从库的命令会有一个下标.如果从库断连后重连,则会告诉主库最后一个下标,并从该下标开始追赶执行命令同步主库
从新同步时,主库会开启一个存储进程生成RDB文件(写入磁盘),并缓存新接收的写命令.将RDB文件发送给从库,从库加载RDB文件到内存中,并接收主库缓存的命令然后继续同步
从库不会对key做expire操作,当主库key过期时执行del操作时发送到从库,从库执行
当访问从库已过期key时,因为主库的延迟操作,从库根据自身时钟做出判断报告该key不存在
主库执行Lua脚本时,时间被冻结,所以脚本必须同步到从库执行保证一致效果
7. Redis Persistence
7.1 模式
RDB: 通过对某个时间点数据集存储为快照文件
AOF: 记录每一个写操作,通过重放方式初始化数据
可以在同一个实例中同时使用这两种模式,重启实例时,AOF模式用于从新初始化数据
7.2 RDB
随心所欲对某个时间点的数据进行备份
适合用于灾难恢复
通过子进程完成备份,而父进程不用磁盘IO,不影响其他命令的进行
重启初始化数据更快
派生子进程,在数据集很大时会比较耗时.频繁备份对性能有一定损耗
7.3 AOF
可以多种文件同步策略,每秒记录或每个写命令时记录,持续性更好
是一个只加文件,无需定位,容易修复
如果AOF文件太大时,redis会自动生成新的文件并切换到新的文件
如果不小心执行了FLUSHALL命令,只要还没有新的命令写入,停止实例.并将最后一个命令删除后重启redis就可以了
7.4 备份数据
redis会避免RDB和AOF的进程在同一时刻进行
建议设置定时任务生成每个小时的RDB文件放在一个文件夹和生成每天的RDB文件放在另一个文件夹
定时任务每次执行,清除比较老的RDB文件
每天转移RDB文件
8. Redis Security
网络安全策略,将redis运行在虚拟化的linux实例避免直接暴露,外部无法通过防火墙连接redis,客户端通过环回地址与对应端口通信
安全模式: 从3.2.0版本开始,如果在配置上允许绑定所有端口,而且外部访问无需密码时会进入安全模式,只有通过环回地址的才能够正常访问,其他的客户端返回错误
认证功能: 密码应该设置足够长
数据加密: redis并不支持,不过可以再加一层SSL代理.redis推荐Spiped做对称加密
设置一些命令不可用
NoSQL注入: 注意从不可信赖来源获取可能为Lua脚本作为字符串的问题
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。